package chabernac.gravidroid.model;

import java.util.ArrayList;
import java.util.List;

public class StructureFactory {
    private static final float strenght = 3000;
    private static final float speedResistance = 20;

    public static Structure createRectangle(Vector aTopLeft, Vector aBottomRight, int aColor, Vector aGravity, float aMass) {
        Structure theStructure = new Structure();
        Vector theHorizontalVector = new Vector(aBottomRight.x - aTopLeft.x, 0);
        Vector theVerticalVector = new Vector(0, aBottomRight.y - aTopLeft.y);
        Vector theTopRight = new Vector();
        aTopLeft.add(theHorizontalVector, theTopRight);
        Vector theBottomLeft = new Vector();
        aTopLeft.add(theVerticalVector, theBottomLeft);

        List<Vector> thePoints = new ArrayList<Vector>();
        thePoints.add(aTopLeft);
        thePoints.add(theTopRight);
        thePoints.add(aBottomRight);
        thePoints.add(theBottomLeft);

        Mass thePreviousMass = null;
        for (Vector thePoint : thePoints) {

            Mass theMass = new Mass()
            .setRadius(5)
            .setMass(aMass)
            .setColor(aColor)
            .setMoveable(true)
            .setLocation(thePoint)
            .setResistanceToSpeed(0);

            if (aGravity != null) {
                theMass.setGravity(aGravity);
            }

            if (thePreviousMass != null) {
                theStructure.addLinearMass(new LinearMass(thePreviousMass, theMass, strenght, speedResistance));
            }
            thePreviousMass = theMass;
        }
        theStructure
        .addLinearMass(new LinearMass(thePreviousMass, theStructure.getLinearMasses().get(0).getMass1(), strenght, speedResistance));

        theStructure.addInteralLinearMass(new LinearMass(theStructure.getLinearMasses().get(0).getMass1(), theStructure
                                                         .getLinearMasses()
                                                         .get(1)
                                                         .getMass2(), strenght, speedResistance));

        theStructure.addInteralLinearMass(new LinearMass(theStructure.getLinearMasses().get(1).getMass1(), theStructure
                                                         .getLinearMasses()
                                                         .get(2)
                                                         .getMass2(), strenght, speedResistance));

        theStructure.setColor(aColor);
        return theStructure;

    }

    public static Structure createTriangle(Vector aLocation, int aRadius, float aMass, int aColor, Vector aGravity) {
        float forceStrength = 3000;
        float speedResistance = 20f;
        Structure theStructure = new Structure();
        float theAngleStep = (float) (2 * Math.PI / 3);
        float angle = 0;
        Mass thePreviousMass = null;
        for (int i = 0; i < 3; i++, angle += theAngleStep) {
            float x = (float) Math.cos(angle) * aRadius;
            float y = (float) Math.sin(angle) * aRadius;
            Vector theLocation = new Vector(x, y);
            theLocation.add(aLocation, theLocation);

            Mass theMass = new Mass()
            .setRadius(5)
            .setMass(aMass)
            .setColor(aColor)
            .setMoveable(true)
            .setLocation(theLocation)
            .setResistanceToSpeed(0);

            if (aGravity != null) {
                theMass.setGravity(aGravity);
            }

            if (thePreviousMass != null) {
                theStructure.addLinearMass(new LinearMass(thePreviousMass, theMass, forceStrength, speedResistance));
            }
            thePreviousMass = theMass;
        }

        // tie the last mass with the first
        List<LinearMass> theLinearMasses = theStructure.getLinearMasses();
        Mass theLastMass = theLinearMasses.get(theLinearMasses.size() - 1).getMass2();
        Mass theFirstMass = theLinearMasses.get(0).getMass1();
        theStructure.addLinearMass(new LinearMass(theLastMass, theFirstMass, forceStrength, speedResistance));
        theStructure.setColor(aColor);

        theStructure = cutOffSharpEdges(theStructure, 200);
        theStructure.createInternalLinearMasses(2, forceStrength * 2, speedResistance * 2);
        return theStructure;
    }

    public static Structure cutOffSharpEdges(Structure aStructure, float aMaxDistancePerFrame) {
        System.out.println("Cutting off sharp edges");
        List<LinearMass> theLinearMasses = new ArrayList<LinearMass>(aStructure.getLinearMasses());
        // add the first one as last element so that we will also inspect the angle
        // between the last and first linearmass
        theLinearMasses.add(theLinearMasses.get(0));
        aStructure.getLinearMasses().clear();

        LinearMass thePrevious = null;
        for (LinearMass theLinearMass : theLinearMasses) {
            if (thePrevious != null) {
                aStructure.addLinearMass(thePrevious);
                // if the angle between the 2 vectors is sharp then we must introduce a
                // new vertex
                Vector theV1 = thePrevious.getVector();
                // v1 if pointing towards the common point, so we invert the vector
                theV1.multiply(-1, theV1);
                Vector theV2 = theLinearMass.getVector();
                float theAngle = theV1.getAngle(theV2);
                if (theAngle < Math.PI / 2) {
                    float theDistanceFromCommonPoint = (aMaxDistancePerFrame / 2) / (float) Math.sin(theAngle / 2);

                    // normalize the vectors
                    theV1.normalize(theV1);
                    theV1.multiply(theDistanceFromCommonPoint, theV1);
                    theV2.normalize(theV2);
                    theV2.multiply(theDistanceFromCommonPoint, theV2);

                    // we can now obtain the 2 new points by adding both vectors to the
                    // common point
                    Vector theP1 = new Vector();
                    thePrevious.getMass2().location.add(theV1, theP1);

                    Vector theP2 = new Vector();
                    thePrevious.getMass2().location.add(theV2, theP2);

                    thePrevious.getMass2().mass = thePrevious.getMass2().mass / 2;
                    thePrevious.getMass2().location = theP1;

                    Mass theNewMass = thePrevious.getMass2().copy();
                    theNewMass.location = theP2;

                    LinearMass theNewLinearMass = new LinearMass(thePrevious.getMass2(), theNewMass, thePrevious.getStrength(),
                                                                 thePrevious.getSpeedResistance());
                    aStructure.addLinearMass(theNewLinearMass);
                    theLinearMass.setMass1(theNewMass);

                }
            }
            thePrevious = theLinearMass;
        }
        aStructure.recalculateRestDistances();
        // aStructure.redefineInternalMasses();
        aStructure.printLines();
        return aStructure;
    }
}
